package cn.com.easy.utils;

import java.awt.Container;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.imageio.ImageIO;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import cn.com.easy.exception.BusinessException;

/**
 * 文件上传工具类
 * 
 * @author nibili 2017年4月11日
 * 
 */
public class UploadFileUtils {

	private static Logger logger = LoggerFactory.getLogger(UploadFileUtils.class);

	/** 文件根路径 */
	public static String rootPath = "/home/data/static/upload/";

	/**
	 * 删除硬盘文件
	 * 
	 * @param uploadFileVo
	 * @author nibili 2017年4月11日
	 */
	public static void deleteFile(UploadFileVo uploadFileVo) {
		String root = rootPath;
		// 在硬盘删除原文件
		if (uploadFileVo != null) {
			try {
				String fullPath = root + File.separator + uploadFileVo.getPath() + File.separator + uploadFileVo.getName();
				File f = new File(fullPath);
				if (f.exists()) {
					logger.info("删除硬盘文件:" + fullPath);
					FileUtils.forceDelete(f);
				}
			} catch (Throwable e) {
				logger.error(e.getMessage(), e);
			}
		}
	}

	/**
	 * 上传文件：已经保存到硬盘，等待把返回的对象保存到数据库
	 * 
	 * @param fileUploadVO
	 * @param accessory
	 *            原文件
	 * @return
	 * @throws BusinessException
	 */
	public static UploadFileVo uploadFile(UploadFileDto uploadFileDto) throws BusinessException {

		if (uploadFileDto == null) {
			throw new BusinessException("上传对象不能为空");
		}
		if (uploadFileDto.getStorageFilePath() == null) {
			throw new BusinessException("存储文件路径不能为空");
		}
		if (uploadFileDto.getStorageFileName() == null) {
			throw new BusinessException("存储文件名不能为空");
		}
		if (uploadFileDto.getFile() == null) {
			throw new BusinessException("文件不能为空");
		}
		try {
			String root = rootPath;
			// 文件大小1024*1024*10 => 10M
			byte[] data = uploadFileDto.getFile().getBytes();
			if (data == null || data.length == 0) {
				if (uploadFileDto.getLimitFileSize() != null) {
					throw new BusinessException("文件为空");
				}
				if (data.length > uploadFileDto.getLimitFileSize()) {
					throw new BusinessException("文件大小超过限制大小");
				}
			}
			// 获取文件扩展名
			String origName = uploadFileDto.getFile().getOriginalFilename();
			String extend = origName.substring(origName.lastIndexOf(".") + 1).toLowerCase();
			// 文件存储路径格式：/ROOT/module/submodule/yyyy-mm-dd/
			String filePath = uploadFileDto.getStorageFilePath();
			String filePathWithRoot = root + "/" + filePath;
			File directory = new File(filePathWithRoot);
			if (!directory.exists()) {
				FileUtils.forceMkdir(directory);
			}
			String fileName = uploadFileDto.getStorageFileName() + "." + extend;
			File uploadedFile = new File(directory, fileName);
			// 返回保存数据库对象
			UploadFileVo accessory = new UploadFileVo();
			accessory.setPath(filePath);
			accessory.setName(fileName);
			accessory.setSize(uploadFileDto.getFile().getBytes().length);
			accessory.setExt(extend);
			try {
				// 图片处理
				BufferedImage bis = ImageIO.read(uploadedFile);
				int w = bis.getWidth();
				int h = bis.getHeight();
				accessory.setWidth(w);
				accessory.setHeight(h);
			} catch (Exception ex) {
				try {
					Image image = null;
					image = Toolkit.getDefaultToolkit().getImage(uploadedFile.getPath());
					MediaTracker mediaTracker = new MediaTracker(new Container());
					mediaTracker.addImage(image, 0);
					mediaTracker.waitForID(0);
					accessory.setWidth(image.getWidth(null));
					accessory.setHeight(image.getHeight(null));
				} catch (Exception e1) {
					logger.error("获取图片宽和高异常：" + fileName, e1);
				}
			}
			if (uploadFileDto.getLimitMinWidth() != null) {
				if (accessory.getWidth() < uploadFileDto.getLimitMinWidth().intValue()) {
					if (uploadFileDto.getLimitMaxWidth() != null) {
						throw new BusinessException("当前图片宽度为" + accessory.getWidth() + "px,必须在[" + uploadFileDto.getLimitMinWidth() + "到" + uploadFileDto.getLimitMaxWidth()
								+ "之间]");
					}
					throw new BusinessException("当前图片宽度为" + accessory.getWidth() + ",必须大于[" + uploadFileDto.getLimitMaxWidth() + "px]");
				}
			}
			if (uploadFileDto.getLimitMaxWidth() != null) {
				if (accessory.getWidth() > uploadFileDto.getLimitMaxWidth().intValue()) {
					if (uploadFileDto.getLimitMinWidth() != null) {
						throw new BusinessException("当前图片宽度为" + accessory.getWidth() + "px,要求在[" + uploadFileDto.getLimitMinWidth() + "到" + uploadFileDto.getLimitMaxWidth()
								+ "之间]");
					}
					throw new BusinessException("当前图片宽度为" + accessory.getWidth() + ",必须小于[" + uploadFileDto.getLimitMaxWidth() + "px]");
				}
			}
			// 写入硬盘
			FileUtils.writeByteArrayToFile(uploadedFile, data);
			return accessory;
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
			throw new BusinessException(e.getMessage(), e);
		}
	}

	/**
	 * @Descriptionmap 对字节数组字符串进行Base64解码并生成图片
	 * @author temdy
	 * @Date 2015-01-26
	 * @param base64
	 *            图片Base64数据
	 * @param path
	 *            图片路径
	 * @return
	 */
	public static boolean base64ToImage(String base64, String path, String imgName) {// 对字节数组字符串进行Base64解码并生成图片
		try {
			FileUtils.writeByteArrayToFile(new File(rootPath + path + "/" + imgName), generateImage(base64));
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	public static void main(String[] args) {
		System.out.println(imageToBase64("/Users/Xzz/Desktop/素材/friend.jpg"));
		// base64ToImage(imageToBase64("C:\\home\\data\\static\\upload\\app\\deee7923-dc17-4fcc-b18b-d24ad4a76e34.jpg"),
		// "","123.png");
	}

	/**
	 * 将图片文件转化为字节数组字符串，并对其进行Base64编码处理
	 * 
	 * @author temdy
	 * @Date 2015-01-26
	 * @param path
	 *            图片路径
	 * @return
	 */
	public static String imageToBase64(String path) {// 将图片文件转化为字节数组字符串，并对其进行Base64编码处理
		byte[] data = null;
		// 读取图片字节数组
		try {
			InputStream in = new FileInputStream(path);
			data = new byte[in.available()];
			in.read(data);
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		// 对字节数组Base64编码
		BASE64Encoder encoder = new BASE64Encoder();
		return encoder.encode(data);// 返回Base64编码过的字节数组字符串
	}

	/**
	 * base64字符串转化成图片
	 * 
	 * @param imgStr
	 * @return
	 * @author nibili 2016年9月9日
	 */
	public static byte[] generateImage(String imgStr) {
		// 对字节数组字符串进行Base64解码并生成图片
		BASE64Decoder decoder = new BASE64Decoder();
		try {
			// Base64解码
			byte[] b = decoder.decodeBuffer(imgStr);
			for (int i = 0; i < b.length; ++i) {
				if (b[i] < 0) {// 调整异常数据
					b[i] += 256;
				}
			}
			return b;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 上传文件vo
	 * 
	 * @author nibili 2017年4月11日
	 * 
	 */
	public static class UploadFileVo {

		/** 文件相对路径 */
		private String path;
		/** 文件名称 */
		private String name;
		/** 文件格式后缀 */
		private String ext;
		/** 文件大小 */
		private float size;
		/** 图片宽 */
		private int width;
		/** 图片高 */
		private int height;

		/**
		 * get 文件相对路径
		 * 
		 * @return
		 * @author nibili 2017年4月11日
		 */
		public String getPath() {
			return path;
		}

		/**
		 * set 文件相对路径
		 * 
		 * @param path
		 * @author nibili 2017年4月11日
		 */
		public void setPath(String path) {
			this.path = path;
		}

		/**
		 * get 文件名称
		 * 
		 * @return
		 * @author nibili 2017年4月11日
		 */
		public String getName() {
			return name;
		}

		/**
		 * set 文件名称
		 * 
		 * @param name
		 * @author nibili 2017年4月11日
		 */
		public void setName(String name) {
			this.name = name;
		}

		/**
		 * get 文件格式后缀
		 * 
		 * @return
		 * @author nibili 2017年4月11日
		 */
		public String getExt() {
			return ext;
		}

		/**
		 * set 文件格式后缀
		 * 
		 * @param ext
		 * @author nibili 2017年4月11日
		 */
		public void setExt(String ext) {
			this.ext = ext;
		}

		/**
		 * get 文件大小
		 * 
		 * @return
		 * @author nibili 2017年4月11日
		 */
		public float getSize() {
			return size;
		}

		/**
		 * set 文件大小
		 * 
		 * @param size
		 * @author nibili 2017年4月11日
		 */
		public void setSize(float size) {
			this.size = size;
		}

		/**
		 * get 图片宽
		 * 
		 * @return
		 * @author nibili 2017年4月11日
		 */
		public int getWidth() {
			return width;
		}

		/**
		 * set 图片宽
		 * 
		 * @param width
		 * @author nibili 2017年4月11日
		 */
		public void setWidth(int width) {
			this.width = width;
		}

		/**
		 * get 图片高
		 * 
		 * @return
		 * @author nibili 2017年4月11日
		 */
		public int getHeight() {
			return height;
		}

		/**
		 * set 图片高
		 * 
		 * @param height
		 * @author nibili 2017年4月11日
		 */
		public void setHeight(int height) {
			this.height = height;
		}

	}

	/**
	 * 上传文件dto
	 * 
	 * @author nibili 2017年4月11日
	 * 
	 */
	public static class UploadFileDto {
		/** spring mvc 中的 多媒体文件对象 */
		private MultipartFile file;
		/** 存储文件名 */
		private String storageFileName;
		/** 存储文件路径 */
		private String storageFilePath;
		/** 限制最小宽度，null表示不限制 */
		private Integer limitMinWidth;
		/** 限制最大宽度，null表示不限制 */
		private Integer limitMaxWidth;
		/** 限制最小高度，null表示不限制 */
		private Integer limitMinHeight;
		/** 限制最大高度，null表示不限制 */
		private Integer limitMaxHeight;
		/** 限制文件大小，单位byte,默认50m */
		private Integer limitFileSize = 1024 * 1024 * 50;

		/**
		 * @param rootPath
		 *            根路径
		 * 
		 * @param file
		 *            文件对象
		 * @param storageFilePath
		 *            存储文件的中间块的相对路径，如：{rootpath}/{storageFilePath} /{
		 *            storageFileName}
		 * @param storageFileName
		 *            存储文件的文件名，如：{rootpath}/{storageFilePath}/{ storageFileName}
		 * @param userId
		 *            用户id
		 */
		public UploadFileDto(MultipartFile file, String storageFilePath, String storageFileName) {
			this.file = file;
			this.storageFilePath = storageFilePath;
			this.storageFileName = storageFileName;
		}

		/**
		 * get springmvc中的多媒体文件对象
		 * 
		 * @return
		 * @author nibili 2017年4月11日
		 */
		public MultipartFile getFile() {
			return file;
		}

		/**
		 * set springmvc中的多媒体文件对象
		 * 
		 * @param file
		 * @author nibili 2017年4月11日
		 */
		public void setFile(MultipartFile file) {
			this.file = file;
		}

		/**
		 * get 存储文件名
		 * 
		 * @return
		 * @author nibili 2017年4月11日
		 */
		public String getStorageFileName() {
			return storageFileName;
		}

		/**
		 * set 存储文件名
		 * 
		 * @param storageFileName
		 * @author nibili 2017年4月11日
		 */
		public void setStorageFileName(String storageFileName) {
			this.storageFileName = storageFileName;
		}

		/**
		 * get 存储文件路径
		 * 
		 * @return
		 * @author nibili 2017年4月11日
		 */
		public String getStorageFilePath() {
			return storageFilePath;
		}

		/**
		 * set 存储文件路径
		 * 
		 * @param storageFilePath
		 * @author nibili 2017年4月11日
		 */
		public void setStorageFilePath(String storageFilePath) {
			this.storageFilePath = storageFilePath;
		}

		/**
		 * get 限制最小宽度，null表示不限制
		 * 
		 * @return
		 * @author nibili 2017年4月11日
		 */
		public Integer getLimitMinWidth() {
			return limitMinWidth;
		}

		/**
		 * set 限制最小宽度，null表示不限制
		 * 
		 * @param limitMinWidth
		 * @author nibili 2017年4月11日
		 */
		public void setLimitMinWidth(Integer limitMinWidth) {
			this.limitMinWidth = limitMinWidth;
		}

		/**
		 * get 限制最大宽度，null表示不限制
		 * 
		 * @return
		 * @author nibili 2017年4月11日
		 */
		public Integer getLimitMaxWidth() {
			return limitMaxWidth;
		}

		/**
		 * set 限制最大宽度，null表示不限制
		 * 
		 * @param limitMaxWidth
		 * @author nibili 2017年4月11日
		 */
		public void setLimitMaxWidth(Integer limitMaxWidth) {
			this.limitMaxWidth = limitMaxWidth;
		}

		/**
		 * get 限制最小高度，null表示不限制
		 * 
		 * @return
		 * @author nibili 2017年4月11日
		 */
		public Integer getLimitMinHeight() {
			return limitMinHeight;
		}

		/**
		 * set 限制最小高度，null表示不限制
		 * 
		 * @param limitMinHeight
		 * @author nibili 2017年4月11日
		 */
		public void setLimitMinHeight(Integer limitMinHeight) {
			this.limitMinHeight = limitMinHeight;
		}

		/**
		 * get 限制最大高度，null表示不限制
		 * 
		 * @return
		 * @author nibili 2017年4月11日
		 */
		public Integer getLimitMaxHeight() {
			return limitMaxHeight;
		}

		/**
		 * set 限制最大高度，null表示不限制
		 * 
		 * @param limitMaxHeight
		 * @author nibili 2017年4月11日
		 */
		public void setLimitMaxHeight(Integer limitMaxHeight) {
			this.limitMaxHeight = limitMaxHeight;
		}

		/**
		 * get limitFileSize
		 * 
		 * @return
		 * @author nibili 2017年4月11日
		 */
		public Integer getLimitFileSize() {
			return limitFileSize;
		}

		/**
		 * set limitFileSize
		 * 
		 * @param limitFileSize
		 * @author nibili 2017年4月11日
		 */
		public void setLimitFileSize(Integer limitFileSize) {
			this.limitFileSize = limitFileSize;
		}

	}
}
